#include "CSphere.hpp"
#include "CPlayer.hpp"
#include "CField.hpp"

#include <climits>
#include <iostream>
#include <string>
#include <sstream>
#include <map>
#include <utility>
#include <algorithm>
#include <cassert>

////////////////////////////////////////////////////////////////
// Anonymous
//
namespace
{
	// gen square
	const char* GenerateRange(uint range, uint& side_length)
	{
		assert(range < INT_MAX);

		side_length = range * 2 + 1;

		typedef std::string str;
		static std::vector<str> t_range;

		t_range.resize(range + 1);
		str& current = t_range.at(t_range.size() - 1);

		current.resize(side_length * side_length, '0');
		
		str::iterator it = current.begin();
		for(uint i = 0; i < side_length; i++) // height
			for(uint j = 0; j < side_length; j++) // width
			{
				char v = '0';
				if(i <= side_length / 2)
				{
					if(range - i <= j &&
						j < range + 1 + i)
						v = '1';
				}else
				{
					if(i - range <= j && 
						j < range + side_length - i)
						v = '1';
				}
				*it++ = v;
			}
		
		return current.data();
	}

	char getIndexedChar(uint i)
	{
		if(i < 9)
			return '1' + i;
		else if(i < 10 + 'Z' - 'A')
			return 'A' + i - 9;
		return '*';
	}
}

////////////////////////////////////////////////////////////////
// CSphere::Result
//
CSphere::Result::Result(CSphere& cs, CSphere::Result::list_t& v)
	: Mobs(v), s(cs), mobs(&v)
{
}

void CSphere::Result::print() const
{
	list_t::const_iterator it = Mobs.begin();
	while(it != Mobs.end())
	{
		std::cout << "\tMob : " << *it << std::endl;
	}
}

void CSphere::Result::print(
	const CField& f, 
	bool print_info) const
{

//	const std::string str = ss.str();
	list_t& l = *mobs;

	std::cout << l.at(0) << std::endl;

	for(uint i = 0; i < h; i++)
	{
		for(uint j = 0; j < w; j++)
		{
			char c = ' ';
			if(s.get(x + j, y + i) == '1')
			{
				c = '*';
				for(uint k = 0; k < l.size(); k++)
				{
					if(l[k].x - fx == j &&
						l[k].y - fy == i)
					{
						c = getIndexedChar(k);
					}
				}
			}
			std::cout << c;
		}
		if(print_info == true &&
			i < Mobs.size())
		{
			std::cout << '\t' << i + 1 << ":"
				<< f[(*mobs)[i]].pMob->getPos();
		}
		std::cout << std::endl;
	}
}

////////////////////////////////////////////////////////////////
// CSphere
//
/**
* @class CSphere
*
* This class is wrapping and management for the area of range.
* 
* @par About an area of range
* - The area is 2-dimentional array of char.
*   (@ref CSphere::CSphere(char range_area[N][N]) )
* - An element is char but it will only use as bool now.
*   This specifications is for future and human-readablity.
*   - '0' : out of range.
*   - '1' : in of range.
* - If range_length = 2, express As\n
*   0 0 1 0 0\n
*   0 1 1 1 0\n
*   1 1 <b>1</b> 1 1\n
*   0 1 1 1 0\n
*   0 0 1 0 0\n
*   (Bolded 1 is center point)
* .
*/

/**
* @brief Constructor of CSphere
*
* @param range_length length of range
*/
CSphere::CSphere(uint range_length)
	: range(GenerateRange(range_length, width))
{
}


/**
* @brief Constructor of CSphere
*
* @tparam N Array's size (may set automatically)
* @param range_area[N][N] The char array of the range
*/
template<unsigned int N>
CSphere::CSphere(char range_area[N][N])
	: range(range_area), width(N)
{

}

/**
* @brief Lookup mob on the inside of the range in the field.
*
* @param f Field
* @param cx Point to set as center.
* @param cy Point to set as center.
*
* @return Result of looking up.
*/
CSphere::Result CSphere::apply(
	const CField& f,
	uint cx,
	uint cy)
{
	CSphere::Result::list_t* v = new CSphere::Result::list_t;
	CSphere::Result result(*this, *v);

	uint len = this->width / 2;

	uint // Right and Bottom
		r = std::min(len, f.getWidth() - cx - 1) + 1 + len,
		b = std::min(len, f.getHeight()- cy - 1) + 1 + len;

	// calc the length of the rectangle cut off.
	// xy -> upper-left
	// wh -> length of rectangle without xy.
	result.x = (len > cx)? len - cx: 0;
	result.y = (len > cy)? len - cy: 0;
	result.w = r - result.x;
	result.h = b - result.y;
	result.fx = cx - len + result.x;
	result.fy = cy - len + result.y;

	for(uint i = result.y; i < b; i++)
	{
		uint fy = cy + i - len;
		for(uint j = result.x; j < r; j++)
		{
			if(get(j, i) == '0')
				continue;
			
			uint fx = cx + j - len;
			for(uint k = 0; k < f.getDepth(); k++)
			{
				position_t p = {fx, fy, k};
				CField::Cell c = f[p];
				if(c.pMob != NULL)
				{
					result.mobs->push_back(p);
				}
			}
		}
	}
	return result;
}

/**
* @brief Getter for an element of the area of range
*
* @param x Point in the area
* @param y Point in the area
*
* @return The element of array
*/
const char CSphere::get(uint x, uint y) const
{
	return range[y * width + x];
}

void CSphere::print() const
{
	for(uint i = 0; i < width; i++)
	{
		printLine(i);
	}
}

/// print one line of range
void CSphere::printLine(uint l) const
{
	for(uint i = 0; i < width; i++)
	{
		char c = ' ';
		if(this->get(i, l) != '0')
			c = '*';
		std::cout << c;
	}
	std::cout << std::endl;
}

/// print area of range with ruler
void CSphere::printWithRuler() const
{
	std::cout << ' ' << ' ';
	for(uint i = 0; i < width; i++)
	{
		std::cout << getIndexedChar(i) << ' ';
	}
	std::cout << std::endl;
	for(uint i = 0; i < width; i++)
	{
		std::cout << getIndexedChar(i) << ' ';
		printLine(i);
	}
}

